package com.snda.sc.auth;

import java.io.IOException;
import java.util.concurrent.Semaphore;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.snda.sc.exception.ParameterException;
import com.snda.sc.store.IDatabaseStore;
import com.snda.sc.store.LoginRecord;
import com.snda.sc.utils.Constants.ResultCode;
import com.snda.sc.utils.Logs;

@Component("authProcessor")
public class AuthProcessor implements IAuthProcessor {

	@Resource(type = Auth.class)
	private Auth auth;
	@Value("${sc.env.test}")
	private boolean testEnv;
	@Value("${sc.permit.size}")
	private int permitSize;
	@Resource(name = "databaseStore")
	private IDatabaseStore dbStore;
	private Semaphore permit;

	@PostConstruct
	public void init() {
		permit = new Semaphore(permitSize);
	}

	@Override
	public void process(HttpServletRequest request, HttpServletResponse response) {

		AuthParam param = new AuthParam(request);
		Logs.logAuthInfo("Prepare to auth client: {}", param);
		boolean access = permit.tryAcquire();
		if (!access) {
			AuthResult result = auth.createFailureResult(param);
			result.setResult(ResultCode.AUTH_ERR_NORESOURCE);
			result.setCause("No more resource to auth the request: " + param);
			render(result, request, response);
			return;
		}
		AuthResult result = null;
		try {
			result = auth.doAuth(param);
		} catch (ParameterException e) {
			result = auth.createFailureResult(param);
			result.setResult(ResultCode.AUTH_ERR_PARAM);
			result.setCause("The required parameter: " + e.getMessage() + " is missing.");
		} catch (Exception e) {
			result = auth.createFailureResult(param);
			result.setResult(ResultCode.AUTH_ERR_SYS);
			result.setCause("Unknow application exception.");
			Logs.logException("Unknow Exception.", e);
		} finally {
			permit.release();
		}
		render(result, request, response);
	}

	private void render(AuthResult result, HttpServletRequest request, HttpServletResponse response) {
		if (result.getResult() == ResultCode.AUTH_OK) {
			Logs.logAuthInfo("Auth successfully: {}", result);
			dbStore.record(new LoginRecord(result));
		} else {
			Logs.logFailure("Auth failed: {}.", result);
		}
		result.setTestEnv(testEnv);
		try {
			result.doRender(request, response);
		} catch (IOException e) {
			Logs.logException("Auth response output error.", e);
		}
	}
}
